<?php
/**
 * @property array errors
 */
class SJB_Classifieds_Twitter extends SJB_Function
{
	/**
	 * @var string
	 */
	private $listingTypeID;

	/**
	 * @var string
	 */
	private $template = 'post_to_twitter.tpl';

	/**
	 * @var array
	 */
	private $feedInfo;

	/**
	 * @var SJB_TemplateProcessor
	 */
	protected $tp;

	/**
	 * @var int
	 */
	protected $postedListingsNum = 0;

	/**
	 * @var array
	 */
	protected $errors = array('feed' => array(), 'common' => array());

	public function execute()
	{
		$this->tp 	= SJB_System::getTemplateProcessor();
		$plugin 	= SJB_PluginManager::getPluginByName('TwitterIntegrationPlugin');

		if (SJB_Array::get($plugin, 'active') > 0) {
			$action 	= SJB_Request::getVar('action', false);
			$feedSID 	= SJB_Request::getVar('sid', false);
			$pluginObj 	= new $plugin['name'];

			$this->callAction($action, $feedSID, $pluginObj);
		}
		else {
			echo '"TwitterIntegrationPlugin" Plugin is not active';
		}

		exit();
	}

	public function callAction($action, $feedSID, TwitterIntegrationPlugin $plugin)
	{
		if (method_exists($this, $action))
			if ($feedSID > 0)
				call_user_func_array(array($this, $action), array($feedSID, $plugin));
			else
				echo 'Error: Twitter Feed is not defined';
		else
			$this->defaultAction($plugin);
	}

	/**
	 * @param TwitterIntegrationPlugin $pluginObj
	 */
	public function defaultAction(TwitterIntegrationPlugin $pluginObj)
	{
		$feeds = $pluginObj->getAllFeeds();
		if ($feeds) {
			foreach ($feeds as $feed) {
				$this->resetErrors();
				$this->feedInfo = $feed;
				$this->setListingTypeIDByFeedInfo();
				$listingSIDs = $this->getListingsSIDsToPostByFeed();
				$this->postedListingsNum = 0;
				if ($listingSIDs && count($listingSIDs) >= $feed['update_every']) {
					$this->sendListingsToTwitter($listingSIDs);
				}
				$this->displayResult();
			}
		}
	}

	/**
	 * @param int $feedSID
	 * @param TwitterIntegrationPlugin $pluginObj
	 */
	public function run_manually($feedSID, TwitterIntegrationPlugin $pluginObj)
	{
		$this->feedInfo = $pluginObj->getFeedInfoBySID($feedSID);
		$this->setListingTypeIDByFeedInfo();
		$listingSIDs = $this->getListingsSIDsToPostByFeed();
		$this->postedListingsNum = 0;
		if ($listingSIDs) {
			$this->sendListingsToTwitter($listingSIDs);
		}
		$this->displayResult();
	}

	/**
	 * @param int $feedSID
	 * @param TwitterIntegrationPlugin $pluginObj
	 */
	public function run_manually_check($feedSID, TwitterIntegrationPlugin $pluginObj)
	{
		$this->feedInfo = $pluginObj->getFeedInfoBySID($feedSID);
		$this->setListingTypeIDByFeedInfo();
		$listingSIDs 	= $this->getListingsSIDsToPostByFeed();
		$result 		= count($listingSIDs);
		$postedToday 	= $this->getPostedTodayByFeed();

		$this->tp->assign('foundListingsToPost', $result);
		$this->tp->assign('postedToday', $postedToday);
		$this->displayResult();
	}

	public function displayResult()
	{
		$this->tp->assign('postingLimit', $this->getPostingLimitForFeed());
		$this->tp->assign('feedInfo', $this->feedInfo);
		$this->tp->assign('errors', $this->errors);
		$this->tp->assign('listingTypeID', $this->listingTypeID);
		$this->tp->assign('postedListingsNum', $this->postedListingsNum);
		$this->tp->display('twitter.tpl');
	}

	/**
	 * @param array $listingSIDs
	 * @return bool
	 */
	public function sendListingsToTwitter($listingSIDs)
	{
		try {
			$perDayLimit 		= $this->getPostingLimitForFeed();
			$postedToday 		= $this->getPostedTodayByFeed();
			$zendServiceTwitter = $this->getZendServiceTwitter();

			foreach ($listingSIDs as $listingSID) {
				if ($postedToday >= $perDayLimit) {
					throw new Exception('Posting limit for current feed has been reached');
				}
				try {
					$this->postListingToTwitter($listingSID, $zendServiceTwitter);
					$this->saveListingAsPostedForCurrentFeed($listingSID, $this->feedInfo['sid']);
					$this->postedListingsNum++;
					$postedToday++;
				} catch (TwitterFeedException $ex) {
					$this->registerFeedError($ex->getErrorMessage(), $ex->getParams());
				}
			}

			return true;
		}
		catch (Exception $e) {
			$this->registerCommonError($e->getMessage());
		}
	}

	/**
	 * @return int
	 */
	public function getPostingLimitForFeed()
	{
		$feedPostingLimit = SJB_Array::get($this->feedInfo, 'posting_limit');
		return ((int) $feedPostingLimit) > 0 ? $feedPostingLimit : 1000;
	}

	/**
	 * @return int
	 */
	public function getPostedTodayByFeed()
	{
		$query = 'SELECT count(`listing_sid`) as `posted` FROM `twitter_postings`
					WHERE `feed_sid` = ?n AND (INTERVAL 1 DAY + `date`) > NOW()';
		$result = SJB_DB::queryValue($query, $this->feedInfo['sid']);
		return (int)$result;
	}

	/**
	 * @return Zend_Service_Twitter
	 */
	public function getZendServiceTwitter()
	{
		return TwitterIntegrationPlugin::getZendServiceTwitter($this->feedInfo);
	}

	/**
	 * @param int $listingSID
	 * @param Zend_Service_Twitter $zendServiceTwitter
	 * @return bool
	 * @throws Exception
	 */
	public function postListingToTwitter($listingSID, Zend_Service_Twitter $zendServiceTwitter)
	{
		$post 		= $this->getMessageByListingSIDToPost($listingSID);
		$response 	= $zendServiceTwitter->statusesUpdate($post);

		if (!empty($response->error)) {
			$params = array($listingSID, $response->error);
			$message = 'Error with listing #$param_0 : $param_1';
			throw new TwitterFeedException($message, $params);
		}

		sleep(5);

		return true;
	}

	public function getMessageByListingSIDToPost($listingSID)
	{
		$listing = SJB_ListingManager::getObjectBySID($listingSID);
		if (!$listing instanceof SJB_Listing) {
			$params = array($listingSID);
			$message = 'Listing #$param_0 does not exist in system';
			throw new TwitterFeedException($message, $params);
		}

		$listingInfo = SJB_ListingManager::createTemplateStructureForListing($listing);

		$listingUrl = $this->getListingUrl($listingInfo);
		$link 		= $this->getBitLyLinkForUrl($listingUrl);
		$link 		= " {$link} {$this->feedInfo['hash_tags']}";

		$userInfo = SJB_Array::get($listingInfo, 'user');

		$this->tp->assign('post_template', $this->feedInfo['post_template']);
		$this->tp->assign('listing', $listingInfo);
		$this->tp->assign('user', $userInfo);
		$text = $this->tp->fetch($this->template);

		$post = $text . $link;
		if (strlen($post) > 140) {
			$countStrCut = 140 - strlen($post) - 3;
			$text = substr($text, 0, $countStrCut) . '...';
			$post = $text . $link;
		}

		return $post;
	}

	public function setListingTypeIDByFeedInfo()
	{
		$this->listingTypeID = SJB_ListingTypeManager::getListingTypeIDBySID($this->feedInfo['listing_type_sid']);
	}

	public function getBitLyLinkForUrl($url)
	{
		$urlEncoded = urlencode($url);
		$curl = curl_init("http://api.bit.ly/v3/shorten?login={$this->feedInfo['bitLyUsername']}&apiKey={$this->feedInfo['bitLyAPIKey']}&longUrl={$urlEncoded}&format=txt");
		curl_setopt($curl, CURLOPT_HEADER, 0);
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
		$link = curl_exec($curl);

		$bitlyExceptions = array(
			'RATE_LIMIT_EXCEEDED',
			'INVALID_URI',
		);

		if (in_array($link, $bitlyExceptions)) {
			$params = array($url, $link);
			$message = 'Cant get short bitly link for url="$param_0". Bitly Exception: $param_1';
			throw new TwitterFeedException($message, $params);
		}

		return $link;
	}

	public function getListingUrl($listingInfo)
	{
		$title = $this->getListingTitlePreparedForUrl($listingInfo);
		$lowerListingTypeID = strtolower($this->listingTypeID);
		return SJB_System::getSystemSettings('SITE_URL') . "/display-{$lowerListingTypeID}/{$listingInfo['id']}/{$title}";
	}

	public function getListingTitlePreparedForUrl($listingInfo)
	{
		return preg_replace('/[\\/\\\:*?"<>|%#$\s]/', '_', $listingInfo['Title']) . '.html';
	}

	/**
	 * @return array
	 */
	public function getListingsSIDsToPostByFeed()
	{
		$foundListingsSIDsForFeed = $this->getListingsSIDsByFeedCriteria();
		if (!empty($foundListingsSIDsForFeed)) {
			$postedListingsSIDs = $this->getPostedListingsSIDsForFeed($foundListingsSIDsForFeed);
			foreach ($foundListingsSIDsForFeed as $key => $listingSID) {
				if (in_array($listingSID, $postedListingsSIDs))
					unset($foundListingsSIDsForFeed[$key]);
			}
		}

		return $foundListingsSIDsForFeed;
	}

	/**
	 * @param array $listings
	 * @return array
	 */
	public function getPostedListingsSIDsForFeed(array $listings)
	{
		$postedListingsSIDs = array();
		if (!empty($listings)) {
			$query = 'SELECT `listing_sid` FROM `twitter_postings` WHERE `feed_sid` = ?n AND `listing_sid` IN (?l)';
			$results = SJB_DB::query($query, $this->feedInfo['sid'], $listings);
			foreach ($results as $result)
				array_push($postedListingsSIDs, $result['listing_sid']);
		}

		return $postedListingsSIDs;
	}

	/**
	 * @return array
	 */
	public function getListingsSIDsByFeedCriteria()
	{
		$searchData = $this->prepareSearchData($this->feedInfo['search_data']);
		$searchResultsTP = new SJB_SearchResultsTP($searchData, $this->listingTypeID);
		$searchResultsTP->getChargedTemplateProcessor();
		return $searchResultsTP->found_listings_sids;
	}

	/**
	 * @param $searchData
	 * @return array
	 */
	public function prepareSearchData($searchData)
	{
		$request = unserialize($searchData);
		$request['action'] = 'search';
		$request['listing_type']['equal'] = $this->listingTypeID;
		$request['default_listings_per_page'] = 100;
		$request['default_sorting_field'] = 'activation_date';
		$request['default_sorting_order'] = 'DESC';

		return $request;
	}

	/**
	 * @param int $listingSID
	 * @param int $feedSID
	 */
	public function saveListingAsPostedForCurrentFeed($listingSID, $feedSID)
	{
		SJB_DB::query('INSERT INTO `twitter_postings` SET `feed_sid` = ?n, `listing_sid` = ?n', $feedSID, $listingSID);
	}

	protected function registerCommonError($error)
	{
		array_push($this->errors['common'], $error);
	}

	protected function resetErrors()
	{
		$this->errors = array(
			'feed' => array(),
			'common' => array(),
		);
	}

	/**
	 * @param string $message
	 * @param array $params
	 */
	protected function registerFeedError($message, array $params)
	{
		$error = array(
			'params' => $params,
			'message' => $message,
		);
		array_push($this->errors['feed'], $error);
	}
}

class TwitterFeedException extends Exception
{
	public function __construct($message, array $params)
	{
		$this->errorMessage = $message;
		$this->params = $params;
	}

	public function getParams()
	{
		return $this->params;
	}

	public function getErrorMessage()
	{
		return $this->errorMessage;
	}
}